package com.springstudy.shiro.config;

import com.springstudy.shiro.constants.Constants;
import com.springstudy.shiro.shiro.SessionDAO;
import com.springstudy.shiro.shiro.SessionListener;
import com.springstudy.shiro.shiro.filters.*;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Map;

/**
 * @author chenrongrong
 * @since 2020/3/10 15:30
 */
@Configuration
public class ShiroConfig {

    /**
     * 自定义会话监听器
     * @return
     */
    @Bean
    public SessionListener sessionListener() {
        return new SessionListener();
    }

    /**
     * 自定义会话持久化
     * @return
     */
    @Bean
    public SessionDAO sessionDAO() {
        return new SessionDAO();
    }

    /**
     * 自定义OncePerRequestFilter
     * @return
     */
    //@Bean
    public MyOncePerRequestFilter myOncePerRequestFilter() {
        return new MyOncePerRequestFilter();
    }

    /**
     * 自定义AdviceFilter
     * @return
     */
    //@Bean
    //这里不注册为全局bean，防止spring自动托管导致无法通过shiroFilterFactoryBean设置过滤配置
    public MyAdviceFilter myAdviceFilter() {
        return new MyAdviceFilter();
    }

    /**
     * 自定义PathMatchingFilter
     * @return
     */
    //@Bean
    //这里不注册为全局bean，防止spring自动托管导致无法通过shiroFilterFactoryBean设置过滤配置
    public MyPathMatchingFilter myPathMatchingFilter() {
        return new MyPathMatchingFilter();
    }

    /**
     * 自定义AccessControlFilter
     * @return
     */
    //@Bean
    //这里不注册为全局bean，防止spring自动托管导致无法通过shiroFilterFactoryBean设置过滤配置
    public MyAccessControlFilter myAccessControlFilter() {
        return new MyAccessControlFilter();
    }

    /**
     * 定义任意角色校验Filter
     * @return
     */
    //@Bean
    //这里不注册为全局bean，防止spring自动托管导致无法通过shiroFilterFactoryBean设置过滤配置
    public AnyRolesFilter anyRolesFilter() {
        return new AnyRolesFilter();
    }

    /**
     * securityManager初始化，所有校验需要通过securityManager进行
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager(AuthorizingRealm realm, SessionManager sessionManager, RememberMeManager rememberMeManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRememberMeManager(rememberMeManager);
        securityManager.setSessionManager(sessionManager);
        securityManager.setRealm(realm);

        return securityManager;
    }

    /**
     * 可以设置是否记住登陆，可以自定义RememberManager
     * @return
     */
    @Bean
    public RememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        return rememberMeManager;
    }

    /**
     * 自定义cookie设置
     * @return
     */
    @Bean
    public Cookie cookie() {
        SimpleCookie cookie = new SimpleCookie();
        //必填，设置cooke名称
        cookie.setName(Constants.SESSION_COOKIE_NAME);
        //设置 Cookie 的过期时间，秒为单位，默认 - 1 表示关闭浏览器时过期 Cookie；
        cookie.setMaxAge(-1);
        //设置 Cookie 的路径，默认空，即存储在域名根下
        cookie.setPath("/");
        //如果设置为 true，则客户端不会暴露给客户端脚本代码
        cookie.setHttpOnly(true);

        return cookie;
    }

    /**
     * 设置Session相关设置，可自定义SessionManager
     * @return
     */
    @Bean
    public SessionManager sessionManager(SessionListener sessionListener, SessionDAO sessionDAO) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //设置cookie相关设置
        sessionManager.setSessionIdCookie(cookie());
        //设置Session监听器
        sessionManager.getSessionListeners().add(sessionListener);
        //设置Session持久化
        //sessionManager.setSessionDAO(sessionDAO);
        return sessionManager;
    }

    /**
     * 配置默认拦截器和路由规则，也可以放在配置文件中
     * 调用链顺序由上到下，如果满足filter条件则跳出，否则进入下一个filter
     * @return
     */
    @Bean
    ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
        definition.addPathDefinition("/static", "anon");
        //添加自己的过滤器规则
        definition.addPathDefinition("/login/login", "myfilter1,myfilter2,myfilter3,myfilter4");
        //使用默认的roles过滤器，可以过滤特定的角色
        //与注解中使用角色过滤不同，这里会抛出一个401错误
        //shiro默认的角色过滤器需要满足所有条件才可以，因此如果这里要求的是roles[admin,custom],则用户需要同时拥有两个角色
        //同时这里设置了admin下的接口可使用remember me通过登陆
        definition.addPathDefinition("/admin/**", "roles[admin, custom],login");
        //这里使用自定义的角色过滤器，只需要满足任一角色即可通过
        definition.addPathDefinition("/role/testAnyRolesFilter", "anyRoles");
        //一般的接口可以同时支持login和remember me
        definition.addPathDefinition("/**", "authc");

        return definition;
    }

    /**
     * 设置拦截器的示例bean，需要将securityManager设置到bean中
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
        factory.setSecurityManager(securityManager);
        //把自定义过滤器放入工厂中
        factory.getFilters().put("anyRoles", anyRolesFilter());
        factory.getFilters().put("myfilter1", myOncePerRequestFilter());
        factory.getFilters().put("myfilter2", myAdviceFilter());
        factory.getFilters().put("myfilter3", myPathMatchingFilter());
        factory.getFilters().put("myfilter4", myAccessControlFilter());
        factory.getFilters().put("login", new RememberFilter());


        //这里把上面定义的过滤规则放到过滤器工厂中
        //特别注意，这些过滤器必须没有被spring托管，否则由于shiro的filter优先级过高，会导致这些filter会作为spring的全局filter被优先调用，使得这里的配置失效
        factory.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());
        return factory;
    }
}
